#include <systemc>
#include <tlm>
#include <tlm_utils/simple_initiator_socket.h>
#include <tlm_utils/simple_target_socket.h>

using namespace sc_core;
using namespace sc_dt;
using namespace tlm_utils;
using namespace std;

// Module A 是 Initiator, 其作为事务的发起方
// Initiator 通过 Initiator Socket 发送 Transaction
SC_MODULE(A) {
public:
    SC_CTOR(A) { SC_THREAD(send_hello); }
    // 简化的 Initiator Socket
    tlm_utils::simple_initiator_socket<A> socket;

private:
    void send_hello() {
        // Payload 是在发起者和目标之间传输的数据包, 其本质是一个封装了单次事务所需信息的对象.
        // tlm_generic_payload 是 TLM2.0 标准中定义的通用事务类型.
        auto *payload = new tlm::tlm_generic_payload();
        assert(payload != nullptr);

        // 设置传输的数据
        const char *data { "Hello, SystemC TLM2.0!" };
        payload->set_data_ptr((unsigned char *) data);

        // 传输延迟, 由 Target 在处理事务时进行修改
        sc_time delay = SC_ZERO_TIME;

        // 发起阻塞传输事务
        printf("@%s\t[%s] SEND: %s\n", sc_time_stamp().to_string().c_str(), this->name(), data);
        socket->b_transport(*payload, delay);

        // 等待传输延迟
        wait(delay);
        printf("@%s\t[%s] DONE\n", sc_time_stamp().to_string().c_str(), this->name());

        // 释放 payload 对象
        delete payload;
    }
};

// Module B 是 Target, 其为事务的接收方和响应方
// Target 通过 Target Socket 接收 Transaction
SC_MODULE(B) {
public:
    SC_CTOR(B) {
        // 注册回调函数
        // 当 Target Socket 接收到 `b_transport` 请求时, 会调用 B::recv_hello 方法进行处理
        socket.register_b_transport(this, &B::recv_hello);
    }
    // 简化的 Target Socket
    // <B> 是模板参数, 其作用是在编译时指定 simple_target_socket 将与 B 的成员函数进行交互(即注册回调函数)
    tlm_utils::simple_target_socket<B> socket;

private:
    void recv_hello(tlm::tlm_generic_payload &payload, sc_core::sc_time &delay_time) {
        // 从 payload 中获取数据指针, 并重新解释为字符串
        const char *data = reinterpret_cast<const char *>(payload.get_data_ptr());
        printf("@%s\t[%s] RECV: %s\n", sc_time_stamp().to_string().c_str(), this->name(), data);
        // 模拟处理时间, Initiator 会等待该时间
        delay_time += sc_time(2, SC_NS);
    }
};

int sc_main(int argc, char* argv[]) {
    // 实例化模块并绑定 Socket
    A a { "A" };
    B b { "B" };
    a.socket.bind(b.socket);

    // 启动仿真
    sc_start(10, SC_NS);

    return EXIT_SUCCESS;
}
